/**
 * @file bsp_speaker_handler.c
 * @author liufeng (liufeng@injoinic.com)
 * @brief wt588F02B语音模块handler
 * @version 0.1
 * @date 2024-12-13
 *
 * @copyright Copyright (c) 2024  injoinic
 *
 */

#include "bsp_speaker_handler.h"
#include "stdio.h"

#ifdef USE_FULL_ASSERT
#define assert_failed(char, int)              \
    do {                                      \
        printf("Error:%s,%d\r\n", char, int); \
        while (1) {                           \
        }                                     \
    } while (0)
#define assert_param(expr) \
    if (!expr)             \
    assert_failed(__FILE__, __LINE__)
#else
#define assert_param(expr) ((void)0U)
#endif

void spk_construct(spk_handler_typedef*);
void spk_executor_task();
void spk_waitfinish_task();

spk_handler_typedef spk_handler;                   // spk handler实例对象
static TaskHandle_t executor_task_handle = NULL;   // executor task句柄
static TaskHandle_t waitfinish_task_handle = NULL; // waitfinish task句柄
SemaphoreHandle_t is_preempt = NULL;               // 用于标志是否有插队
SemaphoreHandle_t dummy_idle = NULL;               // 用于标志虚假播放完成

/**
 * @brief 整体逻辑处理
 */
void spk_handler_task()
{
    spk_construct(&spk_handler); // 初始化构造

    xTaskCreate(spk_executor_task, "spk_executor_task", 1024, NULL, 3, &executor_task_handle);
    xTaskCreate(
        spk_waitfinish_task, "spk_waitfinish_task", 1024, NULL, 3, &waitfinish_task_handle
    );
    is_preempt = xSemaphoreCreateBinary();
    dummy_idle = xSemaphoreCreateBinary();

    configASSERT(executor_task_handle);
    configASSERT(waitfinish_task_handle);
    configASSERT(is_preempt);
    configASSERT(dummy_idle);

    xSemaphoreTake(is_preempt, 0);  // 先获取一次，将其置0
    xSemaphoreTake(dummy_idle, 0);
    uint8_t avaliable_msg = 0;      // 消息队列中元素数量
    list_voice_node* node_a = NULL; // 增加结点
    list_voice_node* node_d = NULL; // 删除结点
    list_voice_node* node_t = NULL; // 最高优先级结点
    uint8_t find_higher = 0;        // 是否有更高优先级结点

    while (1) {
        // 1.如果消息队列中有结点，则添加新结点
        avaliable_msg = uxQueueMessagesWaiting(spk_handler.voice_add_queue);
        for (uint8_t i = 0; i < avaliable_msg; i++) {
            xQueueReceive(spk_handler.voice_add_queue, &node_a, portMAX_DELAY);
            spk_handler.priority_list.pf_add_node(&(spk_handler.priority_list), node_a);
        }

        // 2.语音播放完毕，删除结点
        avaliable_msg = uxQueueMessagesWaiting(spk_handler.voice_finish_queue);
        for (uint8_t i = 0; i < avaliable_msg; i++) {
            xQueueReceive(spk_handler.voice_finish_queue, &node_d, portMAX_DELAY);
            spk_handler.priority_list.pf_delete_node(&(spk_handler.priority_list), node_d);
        }

        // 3.如果删除结点，或者增加更高优先级结点，则播放最高优先级结点
        find_higher = spk_handler.priority_list.highest_priority <
                      spk_handler.priority_list.current_priority;

        if (find_higher || (avaliable_msg != 0)) {
            // 取最高优先级链表首结点地址
            node_t = spk_handler.priority_list.pf_first_node(&(spk_handler.priority_list));
            if (node_t != NULL) {
                // 发送给executor去处理
                xQueueSend(spk_handler.voice_executor_queue, &node_t, portMAX_DELAY);
            }
        }

        // 4.如果无结点，优先级设为缺省值，以便后续加入结点的播放
        uint8_t is_empty = spk_handler.priority_list.pf_is_empty(&(spk_handler.priority_list));
        if (is_empty) {
            spk_handler.priority_list.current_priority = 15; // 缺省优先级设为15
            spk_handler.priority_list.highest_priority = 15;
        }

        vTaskDelay(pdMS_TO_TICKS(300)); // 必须有阻塞
    }
}

/**
 * @brief 和语音模块通信
 */
void spk_executor_task()
{
    list_voice_node* node_t;
    uint32_t ticks_500ms = pdMS_TO_TICKS(500);

    while (1) {
        xQueueReceive(spk_handler.voice_executor_queue, &node_t, portMAX_DELAY);

        if (spk_handler.wt588_driver->pf_spk_busy() == 1) {
            xSemaphoreGive(is_preempt);                // 此时busy为低，代表插队
            spk_handler.wt588_driver->pf_stop_play();  // 结束当前播放
            xSemaphoreTake(dummy_idle, portMAX_DELAY); // 等待虚假的busy变高
        }

        // 发送语音的音量和地址
        spk_handler.wt588_driver->pf_set_volume(node_t->volume);
        spk_handler.wt588_driver->pf_start_play(node_t->voice_address);

        // 等待busy有效，代表开始播放
        uint32_t tickStart = xTaskGetTickCount();
        while ((xTaskGetTickCount() - tickStart) < ticks_500ms) {
            if (spk_handler.wt588_driver->pf_spk_busy() == 1) {
                // 更新当前优先级
                spk_handler.priority_list.current_priority =
                    spk_handler.priority_list.highest_priority;
                // 待检测播放完成
                xQueueSend(spk_handler.voice_detect_queue, &node_t, portMAX_DELAY);
                break;
            }
            vTaskDelay(pdMS_TO_TICKS(3));
        }
    }
}

/**
 * @brief 检测busy信号，等待播放完成
 */
void spk_waitfinish_task()
{
    list_voice_node* node_t;

    while (1) {
        xQueueReceive(spk_handler.voice_detect_queue, &node_t, portMAX_DELAY);

        // 等待播放结束,最短高电平实测35ms左右
        while (spk_handler.wt588_driver->pf_spk_busy()) {
            vTaskDelay(pdMS_TO_TICKS(3));
        }

        if (xSemaphoreTake(is_preempt, 0) == pdPASS)
            xSemaphoreGive(dummy_idle);
        else
            xQueueSend(spk_handler.voice_finish_queue, &node_t, portMAX_DELAY);
    }
}

void spk_construct(spk_handler_typedef* spk_handler)
{
    assert_param(spk_handler);

    extern wt588_driver_typedef wt588_instance;
    spk_handler->wt588_driver = &wt588_instance;
    wt588_construct(spk_handler->wt588_driver);
    spk_handler->wt588_driver->pf_init();

    list_handler_construct(&(spk_handler->priority_list));
    spk_handler->voice_add_queue = xQueueCreate(sizeof(list_voice_node*), 5);
    spk_handler->voice_executor_queue = xQueueCreate(sizeof(list_voice_node*), 5);
    spk_handler->voice_detect_queue = xQueueCreate(sizeof(list_voice_node*), 5);
    spk_handler->voice_finish_queue = xQueueCreate(sizeof(list_voice_node*), 5);

    // 确认消息队列创建成功
    assert_param(spk_handler->voice_add_queue);
    assert_param(spk_handler->voice_executor_queue);
    assert_param(spk_handler->voice_detect_queue);
    assert_param(spk_handler->voice_finish_queue);
}
